package il.ac.technion.cs236700.address_book;

import il.ac.technion.cs236700.address_book.Address;
import il.ac.technion.cs236700.address_book.AddressBook;
import il.ac.technion.cs236700.address_book.FakedAddressBook;
import il.ac.technion.cs236700.address_book.Person;
import il.ac.technion.cs236700.address_book.Samples;
import il.ac.technion.cs236700.utils.BadData;
import il.ac.technion.cs236700.utils.Misc;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import org.junit.Assert;
import org.junit.Test;


/**
 * A test class for {@link FakedAddressBook}
 */
public class AddressBook_Test {   
   private static class CsvReader implements Iterable<Map<String,String>> {
      private final String[] keys;
      private final ArrayList<Map<String,String>> rows 
         = new ArrayList<Map<String,String>>();
      
      public CsvReader(String csv) {
         Scanner s = new Scanner(new StringReader(csv));
         
         keys = split(s.nextLine());
         
         List<String> temp = new ArrayList<String>();
         while(s.hasNext())
            temp.add(s.nextLine());
         
         Collections.sort(temp);
         for(String str : temp) {
            String[] row = split(str);
            if(row.length != keys.length)
               throw new BadData("Illegal CSV format");
           
            Map<String,String> map = new HashMap<String,String>();
            for(int i = 0; i < row.length; ++i)
               map.put(keys[i], row[i]);
            rows.add(map);
         }
      }
      
      private static String[] split(String s) {
         return s.split(",");
      }

      @SuppressWarnings("unchecked")
      public Iterator<Map<String, String>> iterator() {
         return ((ArrayList<Map<String,String>>)rows.clone()).iterator();
      }
      
      public String[] keys() {
         return Arrays.copyOf(keys, keys.length);
      }
      
      public String at(int row, String key) {
         return rows.get(row).get(key);
      }
   }
   
   protected static String[] arr(String... elements) {
      return elements;
   }
   
   /**
    * A factory method 
    * @return A new address book object
    */
   protected AddressBook newAddressBook() {
      return new FakedAddressBook();      
   }
   
   @Test
   public void testGetSupportedFields() {
      AddressBook ab = newAddressBook();
      Assert.assertArrayEquals(arr(
         "First Name", 
         "Last Name", 
         "Home Phone",
         "Mobile Phone",
         "Work Phone",
         "Fax",
         "Email",
         "Instant Messanger Type",
         "Instant Messanger ID",
         "Home Address",
         "Work Address",
         "Home page",
         "Title",
         "Company"), ab.getSupportedKeys());
   }
   

   @Test
   public void testGetNormalize() {
      AddressBook ab = newAddressBook();
      ab.setOutput(Samples.FIELDS_1);
      ab.load(Samples.CSV1);
      Assert.assertEquals(Samples.CSV2, ab.getNormalized());
   }

   @Test
   public void testSetOutput() {
      AddressBook ab = newAddressBook();
      ab.setOutput(Samples.FIELDS_2);
      ab.load(Samples.CSV1);
      Assert.assertEquals(Samples.CSV3, ab.getNormalized());
   }


   @Test(expected=BadData.class)
   public void testBadOutput() {
      AddressBook ab = newAddressBook();
      ab.setOutput(new String[] { "Company Web Site" });
   }
   
   @Test
   public void testConflicts() {
      AddressBook ab = newAddressBook();
      ab.setOutput(Samples.FIELDS_3);
      ab.load(Samples.CSV4);
      Assert.assertEquals(Samples.CSV5, ab.getConflicts());
   }      
   
   @Test
   public void testAddMap() {
      AddressBook ab = newAddressBook();
      ab.add(Misc.map("First Name", "Wall-E")
         .map("Home page", "http://www.imdb.com/title/tt0910970").map);
      ab.setOutput(Samples.FIELDS_4);
      Assert.assertEquals(Samples.CSV7, ab.getNormalized());
   }
   
   @Test
   public void testAdd() {
      AddressBook ab = newAddressBook();
      ab.add(new Person("Rick", "Deckard", ""), null, 
         new Address("", "LAPD", "LA", "", ""));
      ab.setOutput(Samples.FIELDS_5);
      
      CsvReader cr = new CsvReader(ab.getNormalized());
      Assert.assertEquals(cr.at(0, "First Name"), "Rick");
      Assert.assertEquals(cr.at(0, "Last Name"), "Deckard");
      Assert.assertEquals(cr.at(0, "Home Address"), "");
      Assert.assertEquals(cr.at(0, "Work Address"), "LAPD LA");      
   }   
}
